package com.pluginsacceptor.acceptor.ui;

import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTable;

import org.apache.log4j.Logger;

import com.pluginsacceptor.acceptor.activator.PluginActivator;
import com.pluginsacceptor.acceptor.activator.PluginList;
import com.pluginsacceptor.acceptor.activator.model.PluginInfo;
import com.pluginsacceptor.acceptor.activator.model.PluginSavedData;
import com.pluginsacceptor.acceptor.activator.model.UiPluginComponent;
import com.pluginsacceptor.acceptor.activator.utils.ActivatorUtils;
import com.pluginsacceptor.acceptor.exception.TechnicalException;
import com.pluginsacceptor.acceptor.ui.info.PluginInfoFrame;

/**
 * Class {@link RootPlaginWindow} contains  two principals methods 1.to create an house for
 * all our plugins and  2. to add activate added plugins in
 *
 * */

public class RootPlaginWindow {
	/**
	 *  {@link Logger} used to log messages about application work
	 *  */
	private static final Logger LOG = Logger.getLogger(RootPlaginWindow.class);
	private JFrame prinicpalWindow;
	private JTable table;
	private JPanel panel;
	private JMenuBar menuBar;
	private JMenu dynamicMenu;

	private JMenuItem itemOfDynamicMenu_addPlugin;

	/** */
	private static String PATH = "/resource/";

	final ImageIcon addIcon = createImageIcon("add_icon.png");

	private JMenuItem itemOfDynamicMenu_ShowAvalablesPlugs;
	final ImageIcon pluginsListIcon = createImageIcon("list_icon.png");

	private JMenuItem itemOfDynamicMenu_load;
	final ImageIcon loadIcon = createImageIcon("load_icon.png");

	private JMenuItem itemOfDynamicMenu_save;
	final ImageIcon saveIcon = createImageIcon("save_icon.png");

	private JMenuItem itemOfDynamicMenu_exit;
	final ImageIcon exitIcon = createImageIcon("exit_icon.png");

	private JMenuItem itemOfDynamicMenu_help;
	final ImageIcon helpIcon = createImageIcon("help_icon.png");

	/** */
	private final List<UiPluginComponent> pluginComponentList = new ArrayList<UiPluginComponent>();

	/*
	 ** Сreation of the main program window
	 */
	public RootPlaginWindow() {
		this.initialize();
	}
	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		this.prinicpalWindow = new JFrame();
		this.prinicpalWindow.setTitle("PLUGIN MANAGER SPREADSHEET");
		this.prinicpalWindow.setBounds(100, 100, 1000, 350);
		this.prinicpalWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.prinicpalWindow.setIconImage(Toolkit.getDefaultToolkit().getImage("imgs/icon.png"));
		this.menuBar = new JMenuBar();
		this.prinicpalWindow.setJMenuBar(menuBar);
		this.dynamicMenu = new JMenu("Menu");
		this.menuBar.add(dynamicMenu);
		this.itemOfDynamicMenu_addPlugin = new JMenuItem("Add new plugin");
		this.itemOfDynamicMenu_addPlugin.setIcon(addIcon);

		this.itemOfDynamicMenu_ShowAvalablesPlugs = new JMenuItem("Show avalables plugins");
		this.itemOfDynamicMenu_ShowAvalablesPlugs.setIcon(pluginsListIcon);

		this.itemOfDynamicMenu_load = new JMenuItem("Load data");
		this.itemOfDynamicMenu_load.setIcon(loadIcon);

		this.itemOfDynamicMenu_save = new JMenuItem("Save data");
		this.itemOfDynamicMenu_save.setIcon(saveIcon);

		this.itemOfDynamicMenu_exit = new JMenuItem("Exit");
		this.itemOfDynamicMenu_exit.setIcon(exitIcon);

		this.itemOfDynamicMenu_help = new JMenuItem("Help");
		this.itemOfDynamicMenu_help.setIcon(helpIcon);

		this.dynamicMenu.add(itemOfDynamicMenu_addPlugin);
		this.dynamicMenu.add(itemOfDynamicMenu_ShowAvalablesPlugs);
		this.dynamicMenu.add(itemOfDynamicMenu_load);
		this.dynamicMenu.add(itemOfDynamicMenu_save);
		this.dynamicMenu.add(itemOfDynamicMenu_exit);
		this.dynamicMenu.add(itemOfDynamicMenu_help);

		this.itemOfDynamicMenu_addPlugin.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				final String path = getChooserPath();
				if (path != null) {
					addPluginToUI(path);
				}
			    itemOfDynamicMenu_save.setEnabled(PluginList.getActivePlugins().size() > 0);
				itemOfDynamicMenu_load.setEnabled(PluginList.getActivePlugins().size() > 0);
			}
		});

		itemOfDynamicMenu_ShowAvalablesPlugs.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				PluginInfoFrame.showPluginInfo(RootPlaginWindow.this);
			}
		});

		itemOfDynamicMenu_save.setEnabled(false);
		itemOfDynamicMenu_save.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				savePlugins();
			}
		});

		itemOfDynamicMenu_load.setEnabled(false);
		itemOfDynamicMenu_load.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				loadPlugins();
			}
		});

		itemOfDynamicMenu_exit.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				LOG.info("Exit");
				System.exit(0);
			}
		});

		itemOfDynamicMenu_help.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				LOG.info("Looking for javadoc file");
				final Desktop desktop = Desktop.getDesktop();
				try {
					desktop.open(new File("doc/index.html" ));
				} catch (final IOException e1) {
	                LOG.error("Loking for javadoc file error", e1);
				}
			}
		});

		panel = new JPanel();
		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
		prinicpalWindow.add(panel, BorderLayout.CENTER);
	}
	/**
	 * Launch the application using
	 */
	public void create() {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				try {
					final RootPlaginWindow window = new RootPlaginWindow();
					window.prinicpalWindow.setVisible(true);
				} catch (final Exception e) {
				    LOG.error("Error", e);
				}
			}
		});
	}

	/**
	 * Add plug-in to UI if it possible
	 * @param path the path to plug-in jar
	 */
	private void addPluginToUI(final String path) {
		try {
			final PluginInfo pluginInfo = PluginActivator.activate(path);
			PluginList.addPlugin(pluginInfo);
			final JComponent pluginComponent = ActivatorUtils.activate(pluginInfo);

			if (pluginComponent != null) {
				panel.add(pluginComponent);
				LOG.info("Added to panel " + pluginInfo.getName() );
			}

			final JComponent pluginMenuComponent = ActivatorUtils.getMenuItem(pluginInfo);
			if (pluginMenuComponent != null) {
				LOG.info("Added to menu " + pluginInfo.getName() );
				this.menuBar.add(pluginMenuComponent);
			}

			pluginComponentList.add(new UiPluginComponent(pluginMenuComponent, pluginComponent));
			ActivatorUtils.addObserverToRequiredPlugin(pluginInfo);
			ActivatorUtils.getObserverFromRequiredPlugin(pluginInfo);
			panel.revalidate();
		} catch (final Exception e1) {
			LOG.error("Activator error", e1);
			JOptionPane.showMessageDialog(
					prinicpalWindow,
					"Invalid file '" + path + "' \n" + e1.getMessage(),
					"Activation error",
					JOptionPane.ERROR_MESSAGE
					);
		}
	}

	/**
	 * Add plug-in to UI if it possible
	 * @param path the path to plug-in jar
	 */
	public void removePluginFromUI(final int position) {

		LOG.debug("uiPluginComponent size is " + this.pluginComponentList.size() + " position is " + position);
		final UiPluginComponent uiPluginComponent = this.pluginComponentList.get(position);

		if (uiPluginComponent.getMenu() != null) {
			this.menuBar.remove(uiPluginComponent.getMenu());
		}
		if (uiPluginComponent.getPanel() != null) {
			panel.remove(uiPluginComponent.getPanel());
		}
		this.panel.repaint();
		this.menuBar.repaint();
		this.prinicpalWindow.repaint();

		this.pluginComponentList.remove(position);
		final PluginInfo pluginInfo = PluginList.getActivePlugins().get(position);
		try {
			ActivatorUtils.deactivate(pluginInfo);
		} catch (final TechnicalException e) {
			LOG.warn("Deactivated error", e);
		}
		PluginList.getActivePlugins().remove(position);

        itemOfDynamicMenu_save.setEnabled(PluginList.getActivePlugins().size() > 0);
        itemOfDynamicMenu_load.setEnabled(PluginList.getActivePlugins().size() > 0);
	}

	/** Save plugins data
	 * @throws TechnicalException */
	private void savePlugins() {
		final JFileChooser fileChooser = new JFileChooser(System.getProperty("user.dir"));
		fileChooser.setSelectedFile(new File("save.ser"));
		if (fileChooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
			final File fileToSave = fileChooser.getSelectedFile();
			try {
				final PluginSavedData savedPluginData = createSavedData();
				serializeData(fileToSave, savedPluginData);
			} catch (final Exception e) {
				LOG.error("Save failed", e);
				JOptionPane.showMessageDialog(
						prinicpalWindow,
						"Can not save data",
						"Save failed",
						JOptionPane.ERROR_MESSAGE
						);
			}
		}



	}

	/** Load plugins data */
	private void loadPlugins() {
		final String fileName = getChooserPath();

		if (fileName != null) {
		    final List<String> notExistPlugin = new ArrayList<String>();
			try {
				final Object loadedObject = deserializeData(fileName);
				if (loadedObject instanceof PluginSavedData) {
					final PluginSavedData pluginSavedData = (PluginSavedData) loadedObject;
					final Map<String, Object> map = pluginSavedData.getPluginData();
					for (final String pluginName : map.keySet()) {
						if (PluginList.contains(pluginName)) {
							final Object load = map.get(pluginName);
							ActivatorUtils.load(PluginList.getPluginInfo(pluginName), load);
						} else {
							LOG.warn("Saved data exists but plugin '" + pluginName + "' doesn't exist");
							notExistPlugin.add(pluginName);
						}
					}
				} else {
					LOG.warn("Invalid load file");
					JOptionPane.showMessageDialog(
							prinicpalWindow,
							"Invalid load file",
							"Load failed",
							JOptionPane.WARNING_MESSAGE
					);
				}

			} catch(final Exception e) {
				LOG.error("Load failed", e);
				JOptionPane.showMessageDialog(
						prinicpalWindow,
						"Can not load data",
						"Load failed",
						JOptionPane.ERROR_MESSAGE
						);
			}

			final StringBuilder notExistPluginMessage = new StringBuilder("");
			if (!notExistPlugin.isEmpty()) {
			    notExistPluginMessage.append("\n File also contains information for plugins:\n");
			    for (int cnt = 0; cnt < notExistPlugin.size(); cnt++) {
			        notExistPluginMessage.append(cnt + 1).append(". ")
			            .append(notExistPlugin.get(cnt)).append("\n");
			    }
			};

			JOptionPane.showMessageDialog(
                prinicpalWindow,
                "Load finished" + notExistPluginMessage,
                "Success load",
                JOptionPane.INFORMATION_MESSAGE
            );
		}
	}

	/**
	 * @return chooser path or null if file doesn't chose
	 */
	private String getChooserPath() {
		final JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		//final JFileChooser chooser = new JFileChooser(System.getProperty("C:/Users/VadymPivak/workspace/PluginsAcceptor/Plugins-acceptor"));
		//final JFileChooser chooser = new JFileChooser("C:/Users/VadymPivak/workspace/PluginsAcceptor/Plugins-acceptor");
//		final JFileChooser chooser = new JFileChooser("Plugins-acceptor");
		//	final JFileChooser chooser = new JFileChooser("./Plugins-acceptor");
//		final JFileChooser chooser = new JFileChooser();

//		chooser.setCurrentDirectory(new File("Plugins-acceptor"));
		final int returnVal = chooser.showOpenDialog(table);
		return (returnVal == JFileChooser.APPROVE_OPTION)
				? chooser.getSelectedFile().getAbsolutePath()
						: null;
	}
	/**
	 * @return
	 * @throws TechnicalException
	 */
	private PluginSavedData createSavedData() throws TechnicalException {
		final PluginSavedData savedPluginData = new PluginSavedData();
		Object saveObject;
		for (final PluginInfo pluginInfo : PluginList.getActivePlugins()) {
			saveObject = ActivatorUtils.save(pluginInfo);
			if (saveObject != null) {
				savedPluginData.addPlugingData(pluginInfo.getName(), saveObject);
			}
		}
		return savedPluginData;
	}

	/**
	 * @param fileName
	 * @throws FileNotFoundException
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	private Object deserializeData(final String fileName)
			throws FileNotFoundException, IOException, ClassNotFoundException {

		final FileInputStream fis = new FileInputStream(fileName);
		final ObjectInputStream ois = new ObjectInputStream(fis);
		try {
			return ois.readObject();
		} finally {
			ois.close();
		}
	}

	/**
	 * @param file the file name
	 * @param savedPluginData the saved data
	 * @throws FileNotFoundException if any errors occurred
	 * @throws IOException if any errors occurred
	 */
	private void serializeData(final File file, final PluginSavedData savedPluginData)
			throws FileNotFoundException, IOException {

		final FileOutputStream fos = new FileOutputStream(file);
		final ObjectOutputStream oos = new ObjectOutputStream(fos);
		try {
			oos.writeObject(savedPluginData);
			oos.flush();
		} finally {
			oos.close();
		}
	}

	/**
	 * Create image from jar resource
	 * @param imageName the image name
	 * @return
	 */
	private ImageIcon createImageIcon(final String imageName) {
		return new ImageIcon(getClass().getResource(PATH + imageName));
	}
}